package com.foreveross.crawl.common.util;



import java.awt.*;
import java.awt.image.*;

import java.io.*;

import java.net.*;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;

import com.gargoylesoftware.htmlunit.BrowserVersion;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.html.DomElement;
import com.gargoylesoftware.htmlunit.html.DomNodeList;
import com.gargoylesoftware.htmlunit.html.HtmlImage;
import com.gargoylesoftware.htmlunit.html.HtmlPage;

/**
 * 数字验证码识别器(用于识别xxx系统的图片验证码)
 * 
 * 算法如下： 分析验证码图片结构，将其分隔成4个独立的数字图片，把四个独立的数字图片处理成单色位图。 
 * 把单色位图转换为0、1数组，然后分别和0-9的字模进行匹配，得到图片上的数字信息。
 * 
 * 注意.这个类暂时只能识别 简单的图片数字
 * 
 * @author lijianning
 * @since JDK 1.6
 * @date 2014-05-20
 * @version 1.0
 */
public class ImageUtils {
	
	//~--- JDK imports ------------------------------------------------------------

	    // 字框高
	    private int intCharHeight = 15;

	    // 字框横向间隙
	    private int intCharSpaceH = 1;

	    // 字框宽
	    private int           intCharWidth = 6;
	    private int           IntImgHeight;
	    private BufferedImage img;
	    private int           intBgColor;
	    private int           intImgWith;
	    //航班号图片解析
	    private final static Map<String, String> codeNo = new HashMap<String, String>();
	    //价格图片解析
	    private final static Map<String, String> codePrice = new HashMap<String, String>();
	    static{
	    	codeNo.put("000000000000000000000111110001000100100010011110001001100100010010001001000101111100000000000000000000000","B");
	    	codeNo.put("000000000000000000000111011001001000101000011000001110000101000010010001001001110110000000000000000000000","K");
	    	codeNo.put("000000000000000000000001110001000100100010010001001000100100010010001001000100011100000000000000000000000","0");
	    	codeNo.put("000000000000000000000000100000110000001000000100000010000001000000100000010000011100000000000000000000000","1");
	    	codeNo.put("000000000000000000000001110001000100100010000001000001000001000001000001000000111110000000000000000000000","2");
	    	codeNo.put("000000000000000000000001110001000100000010000110000000100000010000001001001100011100000000000000000000000","3");
	    	codeNo.put("000000000000000000000000010000011000010100010010001001001000100011110000001000001110000000000000000000000","4");
	    	codeNo.put("000000000000000000000011111001000000100000011110001000100000010010001001000100011100000000000000000000000","5");
	    	codeNo.put("000000000000000000000000111000100100100000010110001100100100010010001001000100011100000000000000000000000","6");
	    	codeNo.put("000000000000000000000011111001001000000100000010000010000001000000100000010000001000000000000000000000000","7");
	    	codeNo.put("000000000000000000000001110001000100100010001010000111000100010010001001000100011100000000000000000000000","8");
	    	codeNo.put("000000000000000000000001110001000100100010010001001001100011010000001001001000111000000000000000000000000","9");
	    	codeNo.put("000000000000000000000000000000000000000000001100000110000000000000000000110000011000000000000000000000000",":");
	    	codeNo.put("000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","NAN");
	    	
	    	
	    	codePrice.put("000000000000000000000000000000000000011100010001001000100100010010001001000100100010010001000111000000000","0");
	    	codePrice.put("000000000000000000000000000000000000001000001100000010000001000000100000010000001000000100000111000000000","1");
	    	codePrice.put("000000000000000000000000000000000000011100010001001000100000010000010000010000010000010000001111100000000","2");
	    	codePrice.put("000000000000000000000000000000000000011100010001000000100001100000001000000100000010010011000111000000000","3");
	    	codePrice.put("000000000000000000000000000000000000000100000110000101000100100010010010001000111100000010000011100000000","4");
	    	codePrice.put("000000000000000000000000000000000000111110010000001000000111100010001000000100100010010001000111000000000","5");
	    	codePrice.put("000000000000000000000000000000000000001110001001001000000101100011001001000100100010010001000111000000000","6");
	    	codePrice.put("000000000000000000000000000000000000111110010010000001000000100000100000010000001000000100000010000000000","7");
	    	codePrice.put("000000000000000000000000000000000000011100010001001000100010100001110001000100100010010001000111000000000","8");
	    	codePrice.put("000000000000000000000000000000000000011100010001001000100100010010011000110100000010010010001110000000000","9");
	    	
	    }	

	    private static String f = "0";
	    // 座标原点
	    private Point  pOrigin;
		public ImageUtils() {
		}
	    
	    public ImageUtils(BufferedImage img) throws IOException {
	        this.img = img;
	        init();
	    }

	    
	    public ImageUtils(File file) throws IOException {
	        img = ImageIO.read(file);
	        init();
	    }

	    
	    public ImageUtils(URL url) throws IOException {
	        img = ImageIO.read(url);
	        init();
	    }

	    
	    private void init() {

	        // 得到图象的长度和宽度
	        intImgWith   = img.getWidth();
	        IntImgHeight = img.getHeight();

	        // 得到图象的背景颜色
	        intBgColor = img.getRGB(7, 4);

	        // System.out.println(intBgColor);

	        // 初始化图象原点座标
	        pOrigin = new Point(0, 0);
	    }

	    private Point[] getCharRange(int intNo) {

	        // 左上右下点座标
	        Point pTopLeft     = new Point(0, 0);
	        Point pBottomRight = new Point(0, 0);

	        // 左上点
	        pTopLeft.x = pOrigin.x + intCharWidth * (intNo - 1) + intCharSpaceH * (intNo - 1);
	        pTopLeft.y = pOrigin.y;

	        // 右下点
	        pBottomRight.x = 1 + pOrigin.x + intCharWidth * intNo + intCharSpaceH * (intNo - 1) - 1;
	        pBottomRight.y = pOrigin.y + intCharHeight - 1;

	        return new Point[] { pTopLeft, pBottomRight };
	    }

	    
	    private char getBit(int x, int y) {
	        int intCurtColor;

	        intCurtColor = img.getRGB(x, y);

	        //System.out.println("[" + x + "," + y + "]" + intCurtColor + "==" + intBgColor + "==>" + (Math.abs(intCurtColor) >7308252));
//	      return (Math.abs(intCurtColor) >= 5689325)
//	              ? '0'
//	              : '1';
	        return (intCurtColor == intBgColor)
	               ? '0'
	               : '1';

	        // 5689325    6008535
	    }

	    
	    private String getCharString(int intNo) {

	        // 本字符的左上右下点座标
	        Point[] p            = getCharRange(intNo);
	        Point   pTopLeft     = p[0];
	        Point   pBottomRight = p[1];

	        // 换算边界值
	        int intX1, intY1, intX2, intY2;

	        intX1 = pTopLeft.x;
	        intY1 = pTopLeft.y;
	        intX2 = pBottomRight.x;
	        intY2 = pBottomRight.y;

//	      System.out.println("intX1=" + intX1);
//	      System.out.println("intY1=" + intY1);
//	      System.out.println("intX2=" + intX2);
//	      System.out.println("intY2=" + intY2);

	        // 在边界内循环取象素
	        int    i, j;
	        String strChar = "";

	        for (i = intY1; i <= intY2; i++) {
	            for (j = intX1; j <= intX2; j++) {
	                strChar = strChar + getBit(j, i);
	            } //System.out.println();
	        }
	      //  System.out.println();

	        return strChar;
	    }

	    
	    public String getNum(int intNo) {
	        // 取得位字符串
	        String strChar = getCharString(intNo);
	        String c ="";
	        try {
	        	if(f=="0"){
	        		 c= codePrice.get(strChar).toString();
	        	}else{
	        		 c= codeNo.get(strChar).toString();
	        	}
	        	
	        	if("NAN".equals(c)){
	        		c = "";
	        	}
			} catch (Exception e) {
				c ="";
			}
	        return c;
	    }

	    
	    public String getValidatecode(int length) {
	        String strNum = "";

	        for (int i = 1; i <= length; i++) {
	            synchronized (this) {
	                strNum += getNum(i);
	            }
	        }

	        return strNum;
	    }

	    
	/**
	 * 测试
	 *
	 * @author  huangyuanmu  2009-7-7
	 * @param args
	 */
	public static void main(String args[]) throws Exception {
		URL url = new URL("http://bk.travelsky.com/bkair/reservation/displayFareImage.do?" +
				"triptype=OW&journeyid=1&flightid=2&cabinid=2");  
		/*BufferedImage img=ImageIO.read(url);
		System.out.println(img);*/
		
		
	}
	/**
	 * 图片解析
	 * @param imgUrl
	 * @throws Exception
	 */
	public String WebClient(HtmlPage queryPage,String imgUrl,String flag) throws Exception {
		
		DomNodeList<DomElement> imgNodes = queryPage.getElementsByTagName("img");
		String c = null;
		for (int j = 0; j < imgNodes.size(); j++) {
			HtmlImage img = (HtmlImage) imgNodes.get(j);
			String src = img.getSrcAttribute();
				if(src.indexOf(imgUrl)>-1){
					ImageReader ir = img.getImageReader();
					ImageUtils imageUtils = new ImageUtils();
					c= imageUtils.writeFile(ir,flag);
				}
		}
		return c;
	}
	public HtmlPage htmlPage(String url) throws Exception{
		WebClient client = new WebClient(BrowserVersion.FIREFOX_24);
		client.getOptions().setJavaScriptEnabled(false); 
		client.getOptions().setCssEnabled(false); 
		client.getOptions().setTimeout(600000); 
		HtmlPage queryPage = client.getPage(new URL(url));
		return queryPage;
	}
	
	public String writeFile(ImageReader reader,String flag) throws IOException {
		f=flag;
		ImageReadParam param = reader.getDefaultReadParam();
		Image image = reader.read(0, param);
		BufferedImage bufferedImage = new BufferedImage(image.getWidth(null), 
				image.getHeight(null), BufferedImage.TYPE_INT_RGB);
		Graphics2D g2 = bufferedImage.createGraphics();
		g2.drawImage(image, null, null);
		File imageFile = new File("../../tmp.png");
		ImageIO.write(bufferedImage, "png", imageFile);//先下载图片
		
		ImageUtils imageUtils = null;
		String code = null;
		if("0".equals(f)){
			 //读取图片文件
			FileInputStream is = new FileInputStream(imageFile); 
			  /*
	        * 返回包含所有当前已注册 ImageReader 的 Iterator，这些 ImageReader 
	        * 声称能够解码指定格式。 参数：formatName - 包含非正式格式名称 .
	        *（例如 "jpeg" 或 "tiff"）等 。 
	        */
	       Iterator<ImageReader> it = ImageIO.getImageReadersByFormatName("png");  
	       ImageReader readerImageReader = it.next(); 
	       //获取图片流 
	       ImageInputStream iis = ImageIO.createImageInputStream(is);
	       /* 
	        * <p>iis:读取源.true:只向前搜索 </p>.将它标记为 ‘只向前搜索’。
	        * 此设置意味着包含在输入源中的图像将只按顺序读取，可能允许 reader
	        *  避免缓存包含与以前已经读取的图像关联的数据的那些输入部分。
	        */
	       readerImageReader.setInput(iis,true) ;
	       
	       /* 
	        * <p>描述如何对流进行解码的类<p>.用于指定如何在输入时从 Java Image I/O 
	        * 框架的上下文中的流转换一幅图像或一组图像。用于特定图像格式的插件
	        * 将从其 ImageReader 实现的 getDefaultReadParam 方法中返回 
	        * ImageReadParam 的实例。  
	        */
	        
	       /*
	        * 图片裁剪区域。Rectangle 指定了坐标空间中的一个区域，通过 Rectangle 对象
	        * 的左上顶点的坐标（x，y）、宽度和高度可以定义这个区域。 
	        */ 
	     Rectangle rect =   new Rectangle(13,0,40,15); //价格图片识别
	       
	       
	         
	       //提供一个 BufferedImage，将其用作解码像素数据的目标。 
	       param.setSourceRegion(rect); 

	       /*
	        * 使用所提供的 ImageReadParam 读取通过索引 imageIndex 指定的对象，并将
	        * 它作为一个完整的 BufferedImage 返回。
	        */
	       BufferedImage bi = readerImageReader.read(0,param);  
	       imageUtils= new ImageUtils(bi);
	       code= imageUtils.getValidatecode(5);
		}else if("1".equals(f)){//航班号识别
			imageUtils= new ImageUtils(imageFile);
			code= imageUtils.getValidatecode(7);
		}else if("2".equals(f)){//时间图片识别
			imageUtils= new ImageUtils(imageFile);
			code= imageUtils.getValidatecode(5);
		}
		imageFile.delete();//读取后再删除图片
		return code;
	}
}